using System;


namespace Gencode.Common.Generics
{
    public class GenLinkedList<T> : IGenerics<T>
    {
        private Node m_firstNode;
		private Node m_lastNode;

        public GenLinkedList()
		{
			m_firstNode = null;
			m_lastNode = null;
		}

		/// <summary>
		/// Adds a new Node T to the list
		/// </summary>
		/// <param name="input">Node to be added</param>
		public void Add(T input) 
		{
			Node n = new Node(input, m_lastNode);

            if (m_lastNode == null)
            {
                m_firstNode = n;
            }
            else
            {
                m_lastNode.Next = n;
            }

			m_lastNode = n;
		}

		/// <summary>
		/// Removes Node T at the given Index
		/// </summary>
		/// <param name="Index">Index of Node to be removed</param>
		public void RemoveAt(int Index)
		{
			Node prev = null;
			Node next = null;
			Node current = m_firstNode;

			int i = 0;
			while (current != null)
			{
				if (i == Index)
				{
					if (current.Previous != null)
					{
                        try
                        {
                            next = current.Next;

                            prev.Next = current.Next;
                            if (next != null)
                                next.Previous = prev;
                        }
                        catch (NullReferenceException e)
                        {
                            System.Console.WriteLine(e.Message);
                        }
                        current = null;
                        
					}
					else
					{
                        if (Count > 1)
                        {
                            try
                            {
                                m_firstNode = m_firstNode.Next;//current.Next;
                                m_firstNode.Previous = null;
                            }
                            catch (NullReferenceException e)
                            {
                                System.Console.WriteLine(e.Message);
                            }
                        }
                        else
                        {
                            //Last Node to be removed
                            m_lastNode = null;
                            m_firstNode = null;
                        }
                        current = null;
					}
				}

                if (current != null)
                {
                    prev = current;
                    current = current.Next;
                    i++;
                }
			}
		}

		/// <summary>
		/// Returns amount of Node T in list
		/// </summary>
		public int Count
		{
			get
			{
				Node current = m_firstNode;
				int i = 0;
				while (current != null)
				{
					i++;
					current = current.Next;
				}
				return i;
			}
		}

		/// <summary>
		/// Clears current List
		/// </summary>
		public void Clear()
		{
			m_firstNode = null;
			m_lastNode = null;
		}

		/// <summary>
		/// Returns Node T at the given Index
		/// </summary>
		/// <param name="Index">Index of Node T</param>
		/// <returns>Node T</returns>
		public T this[int Index]
		{
			get
			{
				int i = 0;
				Node current = m_firstNode;
				while (current != null)
				{
					if (i == Index)
						break;

					current = current.Next;
					i++;
				}
                if (current != null)
                    return current.Data;

                return default(T);
			}
		}

		/// <summary>
		/// Node for generic list
		/// </summary>
		private class Node
		{
            public Node(T t, Node previous)
            {
                next = null;
                data = t;
                this.previous = previous;
            }

			private Node next;
			private Node previous;
			private T data;

			public Node Next
			{
				get { return next; }
				set
				{
					next = value;
				}
			}

			public Node Previous
			{
				get { return previous; }
				set
				{
					previous = value;
				}
			}

			public T Data
			{
				get { return data; }
				set { data = value; }
			}
		}
    }
}
